package p2p;

import java.io.File;
import java.util.Enumeration;
import java.util.Vector;

import kts.TsManager;

import catalogo.ArquivoCatalogo;
import catalogo.Catalogo;
import rice.p2p.commonapi.*;
import rice.pastry.commonapi.PastryIdFactory;
import rice.environment.Environment;

public class SyncFileApp implements Application {
 
	protected Endpoint endpoint;
	protected Node node;
	protected PastryIdFactory localIdFactory;
	private TsManager tsm; 
	public Catalogo catalogo;

	public SyncFileApp(Node node, Environment env, Catalogo catalogo)
	{
		this.endpoint = node.buildEndpoint(this, "syncinstance");
		this.node = node;
		this.endpoint.accept(new SyncFileAppSocketReceiver(this));
		this.endpoint.register();
		this.localIdFactory = new PastryIdFactory(env);
		this.tsm = new TsManager();
		this.catalogo = catalogo;
	}
	
	public Node getNode()
	{
		return node;
	}
	
	public void routeGetTs(String key)
	{
		Id id = localIdFactory.buildId(key);
		SyncMessage msg = new SyncMessage(SyncMessage.GET_TIMESTAMP, key, node.getLocalNodeHandle());
		msg.fromId = this.getNode().getId();
		
		endpoint.route(id, msg, null);
	}
	
	public void routeSendTs(String key, int ts, Id fromId)
	{
		SyncMessage msg = new SyncMessage(SyncMessage.RSP_TIMESTAMP, key, node.getLocalNodeHandle());
		msg.timestamp = ts;
		msg.fromId = this.getNode().getId();
		
		endpoint.route(fromId, msg, null);
	}
	public void routeGetFile(String key, int ts, NodeHandle nh)
	{
		SyncMessage msg = new SyncMessage(SyncMessage.GET_FILE, key, node.getLocalNodeHandle());
		msg.timestamp = ts;
		
		endpoint.route(null, msg, nh);
	}
	
	public void routeCatalogDirect(NodeHandle nh, Catalogo catalog)
	{
		SyncMessage msg = new SyncMessage(SyncMessage.SEND_CATALOG, catalog);
		msg.source = node.getLocalNodeHandle();
		endpoint.route(null, msg, nh);
	}
	
	public void routeSendFile (String filename, NodeHandle nh)
	{
	   endpoint.connect(nh, new SyncFileAppSocketSender(this, filename), 30000);			
	}

	public String toString()
	{
		return "GDD: " + endpoint.getId();
	}
	////////////////////////////////////////////////////////////
	// Metodos da interface
	@Override
	public void deliver(Id id, Message msg) 
	{
		SyncMessage smsg = (SyncMessage)msg;
		
		if (smsg.typeMessage == SyncMessage.SEND_CATALOG)
			System.out.println("Catalogo recebido.");
		else
			System.out.println("Mensagem recebida por "+ catalogo.getrootDir() + " -> "+smsg);
		switch (smsg.typeMessage)
		{
			// request for a timestamp received
			case SyncMessage.GET_TIMESTAMP:			
				int ts = tsm.getTs(smsg.key, catalogo.get_timestamp(smsg.key));
					System.out.println("Enviando timestamp "+ts+ " para o arquivo "+smsg.key);
					routeSendTs(smsg.key, ts, smsg.fromId);
			break;
			
			// timestamp received
			case SyncMessage.RSP_TIMESTAMP:
				System.out.println("Timestamp "+ smsg.timestamp + " recebido para "+smsg.key);
				catalogo.set_timestamp(smsg.key, smsg.timestamp);
				break;
				
			// receiving a catalog
			case SyncMessage.SEND_CATALOG:
					Vector<ArquivoCatalogo> arqsDiffs = catalogo.diffArquivo(smsg.catalogo);
					for (Enumeration<ArquivoCatalogo> arqs = arqsDiffs.elements(); arqs.hasMoreElements();)
					{
						ArquivoCatalogo arqRemoto = arqs.nextElement();
						ArquivoCatalogo arqLocal = catalogo.getArquivos().get(arqRemoto.getNome());

						int remoteTs = arqRemoto.getTimestamp();
						int localTs = arqLocal.getTimestamp();
						if (arqRemoto.getStatus().equals(Catalogo.STATUS_DELETED) && localTs <= remoteTs) 
						{   
							// treat deleted file
							System.out.println("Removendo arquivo localmente: "+arqRemoto.getNome());
							File deletedFile = new File (catalogo.getrootDir(), arqRemoto.getNome());
							// Move the file to the backup
							SyncFileBackup.MoveFileToBackup(deletedFile, remoteTs);
						}						
						else
						{   
							// new or modified file		
							System.out.println("Arquivo solicitado: "+arqRemoto.getNome()+" ao peer "+smsg.source);
							// send file request			
							routeGetFile(arqRemoto.getNome(), arqRemoto.getTimestamp(), smsg.source);
							
							// Falta impedir que um arquivo com status REQUEST_FILE 
							// seja solicitado a outro peer. Nesse caso, deve-se usar  (feito, ver diffArquivo())
							// algum tipo de timeout, para caso o
							// arquivo não seja recebido, ele possa ainda ser atualizado. (por fazer) 
						}
					}
				break;
				
			// received a request for a file
			case SyncMessage.GET_FILE:
					String filename = catalogo.getrootDir()+File.separator+smsg.key; 
					System.out.println("Enviando arquivo "+filename+" para "+smsg.source);
					routeSendFile(filename, smsg.source);
				break;
		}
	}

	@Override
	public boolean forward(RouteMessage arg0) 
	{
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public void update(NodeHandle arg0, boolean arg1) 
	{
		// TODO Auto-generated method stub

	}

}
